#include "pbr_shader.h"
#include "GMaterial.h"
#include "GTexture.h"
#include "GModel.h"
#include "GCamera.h"

float Clamp(float fValue)
{
	if (fValue <= 0)
		return 0;
	if (fValue > 1)
		return 1;
	return fValue;
}

void PBRShader::vertex_shader(VertexProp* vertex, Payload& payload)
{
	vertex->clip_pos = payload.mvp_matrix * vertex->vertex_pos;
	vertex->world_pos = payload.model_matrix * vertex->vertex_pos;
}

float GGX_Distribution(Vector3f n, Vector3f h, float fRoughness)
{
	float alpha_2 = fRoughness * fRoughness;
	float NDotH = n.dot(h);
	NDotH = Clamp(NDotH);
	return alpha_2 / PI / pow((NDotH * NDotH * (alpha_2 - 1) + 1), 2);
}

Vector3f F(Vector3f f0, Vector3f v, Vector3f h)
{
	float hDotV = v.dot(h);
	hDotV = Clamp(hDotV);
	return f0 + (Vector3f(1, 1, 1) - f0) * pow(1 - hDotV, 5);
}

Vector3f CalcF0(Vector3f albedo, float fMetalic)
{
	return fMetalic * albedo + (Vector3f(1, 1, 1) - albedo).cwiseProduct(Vector3f(0.04, 0.04, 0.04));
}

float SchlickGGX(Vector3f n, Vector3f v, float fRoughness)
{
	float k = pow(fRoughness + 1, 2) / 8.0f;
	float nDotV = n.dot(v);
	nDotV = Clamp(nDotV);
	return nDotV / (k + nDotV * (1 - k));
}

Eigen::Vector3f PBRShader::fragment_shader(VertexProp* vertex, Payload& payload)
{
	light light1{ {10, 10, 10},{2, 2, 2} };
	light light2{ {-10, 10, 10},{2, 2, 2} };
	std::vector<light> lights = { light1};

	Vector3f kd = payload.pModel->m_pMaterial->m_pMainTex->SampleTex(vertex->uv.x(), vertex->uv.y());
	//kd = Vector3f(0.13, 0.13, 0.13);
	Vector3f f0 = Vector3f(0.23, 0.23, 0.23);
	float kDiffuse = 0.1f;
	float fSpecular = 1 - kDiffuse;
	float fRoughness = 0.42f;
	float fMetalic = 0.7f;

	Vector3f color = Vector3f(0, 0, 0);
	for (auto light : lights)
	{
		Vector3f l = (light.pos - vertex->world_pos.head<3>()).normalized();
		Vector3f n = vertex->normal.normalized();
		Vector3f v = (payload.pCamera->m_vEye - vertex->world_pos.head<3>()).normalized();
		Vector3f h = (l + v).normalized();
		
		float nDotV = n.dot(v);
		float nDotL = n.dot(l);
		nDotL = Clamp(nDotL);
		nDotV = Clamp(nDotV);
		Vector3f _Fo = CalcF0(kd, fMetalic);
		Vector3f specular = fSpecular * GGX_Distribution(n, h, fRoughness) * F(_Fo, v, h) * SchlickGGX(n, v, fRoughness) / (4 * nDotV * nDotL);
		Vector3f Lo = (kd * kDiffuse + fSpecular * specular).cwiseProduct(light.intensity) * nDotL;

		color += Lo;
	}

	return Vector3f(color.x(), color.x(), color.x());
}